using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using _3DTicTacToe._3D;
using _3DTicTacToe.Core;

namespace _3DTicTacToe
{
    internal partial class GameWindow
    {
        private void resetRotationButton_Click(object sender, RoutedEventArgs e)
        {
            model.Transform = new Transform3DGroup();
        }

        private void resetCameraButton_Click(object sender, RoutedEventArgs e)
        {
            camera.Position = new Point3D(camera.Position.X, camera.Position.Y, 260);
        }

        private void viewport_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _mDown = false;
            resetGrid.Tag = "1";
        }

        private void viewport_MouseLeave(object sender, MouseEventArgs e)
        {
            _mDown = false;
            resetGrid.Tag = "0";
        }

        private void viewport_MouseEnter(object sender, MouseEventArgs e)
        {
            resetGrid.Tag = "1";
        }

        private void viewport_MouseDown(object sender, MouseButtonEventArgs e)
        {
            _mDown = true;
            resetGrid.Tag = "0";

            var pos = Mouse.GetPosition(viewportGrid);
            _mLastPos = new Point(pos.X - viewport.ActualWidth / 2, viewport.ActualHeight / 2 - pos.Y);
        }

        private void viewport_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_mDown) return;

            var pos = Mouse.GetPosition(viewportGrid);
            var actualPos = new Point(pos.X - viewportGrid.ActualWidth / 2,
                                      viewportGrid.ActualHeight / 2 - pos.Y);
            var dx = actualPos.X - _mLastPos.X;
            var dy = actualPos.Y - _mLastPos.Y;
            var mouseAngle = 0D;

            if (dx == 0 || dy == 0)
            {
                if (dx == 0 && dy != 0)
                    mouseAngle = Math.Sign(dy) > 0 ? Math.PI / 2 : Math.PI * 1.5;
                else if (dx != 0 && dy == 0)
                    mouseAngle = Math.Sign(dx) > 0 ? 0 : Math.PI;
            }
            else
            {
                mouseAngle = Math.Asin(Math.Abs(dy) /
                                       Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2)));
                if (dx < 0 && dy > 0) mouseAngle += Math.PI / 2;
                else if (dx < 0 && dy < 0) mouseAngle += Math.PI;
                else if (dx > 0 && dy < 0) mouseAngle += Math.PI * 1.5;
            }

            var axisAngle = mouseAngle + Math.PI / 2;

            var axis = new Vector3D(
                Math.Cos(axisAngle) * 4,
                Math.Sin(axisAngle) * 4, 0);

            var rotation = 0.02 * Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));

            var tgroup = model.Transform as Transform3DGroup;
            var r = new QuaternionRotation3D(new Quaternion(axis, rotation * 180 / Math.PI));

            if (tgroup != null)
                tgroup.Children.Add(new RotateTransform3D(r));

            _mLastPos = actualPos;
        }

        private void viewport_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            camera.Position = new Point3D(camera.Position.X, camera.Position.Y, camera.Position.Z - e.Delta / 20D);
        }

        private static Material CreateMaterial(Color color, double opacity)
        {
            var dbrush = new SolidColorBrush(color) { Opacity = opacity };
            var sbrush = new SolidColorBrush(Colors.Silver) { Opacity = opacity };
            var dmaterial = new DiffuseMaterial(dbrush);
            var smaterial = new SpecularMaterial(sbrush, 5);
            var material = new MaterialGroup();
            material.Children.Add(dmaterial);
            material.Children.Add(smaterial);

            return material;
        }

        private static Point3D Compute3DPositions(int x1, int x2, int x3)
        {
            var x = (x1 == 0) ? -75 : ((x1 == 1) ? -25 : ((x1 == 2) ? 25 : 75));
            var y = (x2 == 0) ? 75 : ((x2 == 1) ? 25 : ((x2 == 2) ? -25 : -75));
            var z = (x3 == 0) ? 75 : ((x3 == 1) ? 25 : ((x3 == 2) ? -25 : -75));

            return new Point3D(x, y, z);
        }

        private void GameOnTurn1(Turn turn)
        {
            Action<Turn> a = t =>
                           {
                               var material = CreateMaterial(t.Player.Color, 0.8);
                               var pos = Compute3DPositions(t.X1, t.X2, t.X3);

                               ModelVisual3D obj;

                               if (turn.Player == _game.PlayerOne)
                               {
                                   var cube = new Cube(35, pos) {Material = material};
                                   obj = cube;
                               }
                               else
                               {
                                   var ball = new Ball(40, pos) {Material = material};
                                   obj = ball;
                               }

                               model.Children.Add(obj);
                           };
            Dispatcher.Invoke(a, turn);
            
        }

        private ModelVisual3D _preview;
        private void ClearPreview3D()
        {
            model.Children.Remove(_preview);
        }

        private void DrawPreview3D(Turn turn)
        {
            ClearPreview3D();

            var material = CreateMaterial(turn.Player.Color, 0.5);
            var pos = Compute3DPositions(turn.X1, turn.X2, turn.X3);

            ModelVisual3D obj;

            if (turn.Player == _game.PlayerOne)
            {
                var cube = new Cube(35, pos) { Material = material };
                obj = cube;
            }
            else
            {
                var ball = new Ball(40, pos) { Material = material };
                obj = ball;
            }

            model.Children.Add(obj);
            _preview = obj;
        }
    }
}